package com.ndp.fb.fbagent.service.webrequest;

import com.ndp.fb.config.ConfigCentre;
import com.ndp.fb.constant.ConfigConst;
import com.ndp.fb.fbagent.service.help.CheckAccessTokenHelper;
import com.ndp.fb.model.fbagent.param.APIPriority;
import com.ndp.fb.redis.api.RedisClient;
import com.ndp.fb.redis.model.RedisParams;
import com.restfb.BinaryAttachment;
import com.restfb.DefaultWebRequestor;
import com.restfb.WebRequestor;
import com.restfb.exception.FacebookJsonMappingException;
import com.restfb.json.JsonException;
import com.restfb.json.JsonObject;
import com.restfb.util.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static com.restfb.util.UrlUtils.urlDecode;

/**
 * Created by Jacky on 2014/12/24.
 */
@Component
@Scope(value = "prototype")
public class AccountQueueWebRequestor extends DefaultWebRequestor {

    private static final org.slf4j.Logger log = LoggerFactory.getLogger("com.ndpmedia.pmd.fb.apilimit");
    private static final org.slf4j.Logger loge = LoggerFactory.getLogger("com.ndpmedia.pmd.fb.apilimitexception");
    private static final org.slf4j.Logger logx = LoggerFactory.getLogger(AccountQueueWebRequestor.class);
    private String urlExceptPattern = "(.*)oauth(.*)|(.*)search(.*)|(.*)/me(.*)|(.*)/userpermissions(.*)|(.*)adaccounts(.*)";
    private String paramExtPattern = "[\\?|\\&](bbbbbb|zzzzzz|yyyyyy|resultClassName)";
    private final static String incrMent = "incrmentzcy";
    private final static int waitResultTimeOut = 300000;


    @Autowired
    private RedisClient redisClient;
    @Autowired
    CheckAccessTokenHelper checkAccessTokenHelper;

    @Override
    public Response executeGet(String url) throws IOException {
        if (ConfigCentre.getBoolean(ConfigConst.FACEBOOK_REQEUST_IS_MOCK)) {
            logx.debug("ismock >>>>>>>> " + true);
            url = url.replace("https://", "http://");
        }
        logx.info(">>>getAccountFromUrl:" + url);

        String result = checkAccessTokenHelper.checkAccessToken(url);
        if (!StringUtils.isBlank(result)) {
            Response response = new WebRequestor.Response(200, result);
            return response;
        }
        String visitUrl = url.split("\\?")[0];
        if (visitUrl.matches(urlExceptPattern)) {
            return super.executeGet(url);
        }

        Response response = productToQueue(url, "get", "", null);
        return checkAccessTokenHelper.checkFBResponse(response, url, null);
    }

    @Override
    public Response executePost(String url, String parameters, BinaryAttachment... binaryAttachments) throws IOException {
        if (ConfigCentre.getBoolean(ConfigConst.FACEBOOK_REQEUST_IS_MOCK)) {
            logx.debug("ismock >>>>>>>> " + true);
            url = url.replace("https://", "http://");
        }
        logx.info(">>>getAccountFromUrl:" + url);

        String result = checkAccessTokenHelper.checkAccessToken(url, parameters);
        if (!StringUtils.isBlank(result)) {
            Response response = new WebRequestor.Response(200, result);
            return response;
        }

        String visitUrl = url.split("\\?")[0];
        if (visitUrl.matches(urlExceptPattern)) {
            return super.executePost(url, parameters, binaryAttachments);
        }
        Response response = productToQueue(url, "post", parameters, binaryAttachments);
        return checkAccessTokenHelper.checkFBResponse(response, url, parameters);
    }

    private Response productToQueue(String url, String method, String parameters, BinaryAttachment[] binaryAttachments) {
        //构造请求参数
        Map<String, String> map = analysisUrl(url);
        String realUrl = map.get("url");
        String accountId = map.get("yyyyyy"); //用户部分参数
        String targetAndEquipment = map.get("zzzzzz"); //消息队列部分参数，target和equipment参数的json
        String code = map.get("bbbbbb"); //优先级参数
        String resultClassName = map.get("resultclassname"); //返回值强转类型

        //计算分数
        boolean isWait = true;
        Double score = APIPriority.Default.getScore();
        APIPriority apiPriority = APIPriority.getAPIPriorityByCode(code);
        Double s = redisClient.incr(incrMent);
        if (apiPriority != null) {
            isWait = apiPriority.isWait();
            score = apiPriority.getScore();
        }
        score = score + s;

        RedisParams redisParams = buildRedisParams(method, parameters, binaryAttachments, realUrl, accountId, targetAndEquipment, isWait, score, resultClassName);

        //// 处理结果部分
        Response response;
        //不等待结果
        if (!isWait) {
            response = new Response(HttpURLConnection.HTTP_OK, "{\"uuid\" :\"" + redisParams.getUuid() + "\"}");
        } else {
            //等待结果返回
            Object obj = loopWaitResult(redisParams);
            if (obj == null) {
                response = new Response(HttpURLConnection.HTTP_GATEWAY_TIMEOUT, null);
            } else {
                response = new Response(HttpURLConnection.HTTP_OK, obj.toString());
            }
//            Long time = System.currentTimeMillis();
            //这里不应该打这种日志 因为请求都是从 quartz中发出去的
//            String traceId = httpUrlConnection.getHeaderField("x-fb-debug");
//            String debugId = httpUrlConnection.getHeaderField("x-fb-trace-id");
//            log.debug("time-_-" + time + "-_-url-_-" + url + "-_-fbaccountid-_-" + accountId+ "-_-traceid-_-" + traceId+ "-_-debugid-_-" + debugId);
//            throwFacebookResponseLimitExceptionIfNecessary(url, time, obj != null ? obj.toString() : null, HttpURLConnection.HTTP_OK);
        }
        return response;
    }

    private RedisParams buildRedisParams(String method, String parameters,
                                         BinaryAttachment[] binaryAttachments, String url,
                                         String accountId, String targetAndEquipment, boolean isWait, Double score, String resultClassName) {
        RedisParams redisParams = new RedisParams();
        redisParams.setAccountId(accountId);
        redisParams.setUrl(url);
        redisParams.setTargetAndEquipment(targetAndEquipment);

        redisParams.setUuid(UUID.randomUUID().toString().replaceAll("-", ""));
        redisParams.setMethodName(method);
        redisParams.setParams(parameters);
        redisParams.setScore(score);
        redisParams.setWait(isWait);
        redisParams.setResultClassName(resultClassName);
        if (binaryAttachments != null && binaryAttachments.length > 0) {
            BinaryAttachment binaryAttachment = binaryAttachments[0];
            if (binaryAttachment != null) {
                redisParams.setFileName(binaryAttachment.getFilename());
                redisParams.setContext(binaryAttachment.getBinaryData());
            }
        }

        String key = "score_act_" + redisParams.getAccountId();
        boolean flag = redisClient.zadd(key, score, redisParams);
        logx.info("请求进入队列  uuid >>> " + redisParams.getUuid() + " ,url: " + url + " ,score:" + score + " , flag:" + flag);

        return redisParams;
    }

    /**
     * add limit request
     *
     * @param redisParams`
     */
    public Object loopWaitResult(RedisParams redisParams) {
        Long beginTime = System.currentTimeMillis();
        logx.info("------------ 等待fb返回结果 --------------------------->>>" + beginTime);
        //accept data
        boolean flag = true;
        Long endTime = 0l;
        Object obj = null;
        while (flag) {
            obj = redisClient.get(redisParams.getUuid());
            if (obj != null) {
                Long times = System.currentTimeMillis();
                logx.info(" ------------------- 等待fb返回结果 返回--------------------------------------------" + times);
                flag = false;
                redisClient.delete(redisParams.getUuid());
            } else {
//                int waitResultTimeOut = Integer.valueOf(PropertyConfigure.getProperty("waitResultTimeOut"));
                //System.out.println(">>>waitResultTimeOut" + String.valueOf(endTime - beginTime));
                if (endTime - beginTime > waitResultTimeOut) {
                    Double min = redisParams.getScore();
                    Double max = redisParams.getScore();
                    //从右边开始选择第一个删除
                    String key = "score_act_" + redisParams.getAccountId();
                    //redisClient.remove(key, -1, redisParams);
                    redisClient.removeRangeByScore(key, min, max);
                    logx.info("------------ 等待fb返回结果超时 --------------------------->>>" + endTime);
                    break;
                }
                try {
                    Thread.sleep(200L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            endTime = System.currentTimeMillis();
        }
        return obj;
    }

    /**
     * 分析本次的url请求
     *
     * @param url
     * @return
     */
    private Map<String, String> analysisUrl(String url) {
        logx.info(">>>analysisUrl:" + url);

        Map<String, String> map = new HashMap<>();
        String[] urls = url.split(paramExtPattern);
        map.put("url", urls[0]);

        String[] params = url.split("[\\?|\\&]");
        for (String param : params) {
            if (param.contains("=")) {
                String[] parsmArray = param.split("=");
                String paramName = parsmArray[0];
                String paramValue = urlDecode(parsmArray[1]);
                logx.info(">>> paramName : " + paramName + " ,  >>> paramValue : " + paramValue);
                map.put(paramName, paramValue);
            }
        }
        if (!map.containsKey("yyyyyy")) {
            logx.warn("请求没有携带用户id url >>>>> " + url);
        }
        return map;
    }

    public RedisClient getRedisClient() {
        return redisClient;
    }

    public void setRedisClient(RedisClient redisClient) {
        this.redisClient = redisClient;
    }

    protected void throwFacebookResponseLimitExceptionIfNecessary(String url, Long time, String json, Integer httpStatusCode) {
        try {
            if (json.startsWith("{")) {
                int e = Math.min(50, json.length());
                if (json.substring(0, e).contains("\"error\"")) {
                    JsonObject errorObject = new JsonObject(json);
                    if (errorObject != null && errorObject.has("error")) {
                        JsonObject innerErrorObject = errorObject.getJsonObject("error");
                        Integer errorCode = innerErrorObject.has("code") ? StringUtils.toInteger(innerErrorObject.getString("code")) : null;
                        Integer errorSubcode = innerErrorObject.has("error_subcode") ? StringUtils.toInteger(innerErrorObject.getString("error_subcode")) : null;
                        loge.debug("time-_-" + time + "-_-Exception-_-errorCode-_-" + errorCode + "-_-errorSubcode-_-" + errorSubcode + "-_-url-_-" + url);
                    }
                }
            }
        } catch (JsonException var10) {
            throw new FacebookJsonMappingException("Unable to process the Facebook API response", var10);
        }
    }
}
